一、权限判断
获取到所有权限进行判断,应该从数据库中获取
public class FilterChainDefinitionMapFactory {
@Autowired
private IPermissionService iPermissionService;
public LinkedHashMap builderFilterChainDefinitionMap(){
LinkedHashMap linkedHashMap = new LinkedHashMap();
//anon 不需要登陆就可以访问 authc需要登陆才可以访问 per[xx:xx]需要授权才可以
linkedHashMap.put("/s/login.jsp", "anon");
linkedHashMap.put("/login", "anon");
linkedHashMap.put("*.js","anon");
linkedHashMap.put("*.css","anon");
linkedHashMap.put("/css/**","anon");
linkedHashMap.put("/js/**","anon");
linkedHashMap.put("/easyui/**","anon");
linkedHashMap.put("/images/**","anon");
//linkedHashMap.put("/dept/index", "perms[dept:index]");
//linkedHashMap.put("/employee/index", "perms[employee:index]");
List list = iPermissionService.findAll();
System.out.println(list);
list.forEach(e->
linkedHashMap.put(e.getUrl(), "permsFilter["+e.getSn()+"]")
);
linkedHashMap.put("/**", "authc");
return linkedHashMap;
}
}
怎么拿到登录用户
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//先拿用户传过来的令牌
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//拿到令牌的用户
String username = token.getUsername();
Employee loginUser = iEmployeeService.findByUsername(username);
if (loginUser==null){
return null;
}
//加盐验证
//要想使用,还需要在xml配置加密次数,和加密算法。。才可以正确解密验证
ByteSource salt = ByteSource.Util.bytes("itsource");
System.out.println(loginUser.getPassword()+"=====================测试=======================");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(loginUser,loginUser.getPassword(),salt,getName());
return simpleAuthenticationInfo;
}
修改页面展示: 主页面的用户现在变成了这个样子 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190713221750217.png)
修改代码:
欢迎[ ]登录,退出
二、完成了登陆验证,将用户存入session
为了以后方便,创建一个工具类专门存取用户
/**
* 该工具可以将当前主体对象存入到session中去
* */
public class UserContext {
public static final String LOGINUSER = "loginUser";
public static void setUser(){
/*先得到全局的授权主体*/
Subject subject = SecurityUtils.getSubject();
//得到session
Session session = subject.getSession();
/*将该主题对象存入到session*/
Employee loginUser = (Employee)subject.getPrincipal();
session.setAttribute(LOGINUSER, loginUser);
}
public static Employee getUser(){
/*先拿到主体employ对象*/
Subject subject = SecurityUtils.getSubject();
//拿到session
Session session = subject.getSession();
//拿到session中的对象
Employee employee = (Employee)session.getAttribute(LOGINUSER);
return employee;
}
}
LoginController:登录成功后把用户放到Session中
@RequestMapping(value="/login",method = RequestMethod.POST)
@ResponseBody
public SuccessBoolean login(String username,String password){
System.out.println("===========================jll");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
if (!subject.isAuthenticated()){
try {
subject.login(token);
} catch (UnknownAccountException e) {
System.out.println("用户名不存在");
return new SuccessBoolean(false,"用户名不存在");
}
catch (IncorrectCredentialsException e) {
System.out.println("密码错误");
e.printStackTrace();
return new SuccessBoolean(false,"用户名不存在或者密码错误");
}
catch (AuthenticationException e) {
System.out.println("未知错误");
e.printStackTrace();
return new SuccessBoolean(false,"未知错误");
}
}
//使用工具存入session中
UserContext.setUser();
//如果登陆验证失败,要访问首页还是会被拦截下来
return new SuccessBoolean();
}
三、根据用户id拿到权限
完成权限的判断 其它完成权限,就是通过当前登录用户拿到所有权限,然后去判断当前用户是否有咱们当前访问的这个路径的权限!
PermissionRepository
@Query("select p.sn from Employee o join o.roles r join r.permissions p where o.id=?1")
Set findPermissionSnByUserID(Long id);
IPermissionService
在这里插入代码片
PermissionServiceImpl
@Override
public Set findPermissionSnByUserID(Long id) {
Set permissions = permissionRepository.findPermissionSnByUserID(id);
return permissions;
}
自定义Realm:进入权限判断
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到主角。。登陆方法验证传过来的是什么 这里就是什么。。
Employee loginUser = (Employee)principalCollection.getPrimaryPrincipal();
//核心管理器会调用doGetAuthorizationInfo方法得到这个用户的权限和角色
//设置到权限的对象并且返回
Set permissions = iPermissionService.findPermissionSnByUserID(loginUser.getId());
//将权限改成从数据库中查找出来。。进行判断
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setStringPermissions(permissions);
//返回权限对象
return authorizationInfo;
}
四、测试,并且完成自定义过滤器
以上功能完成后,我们就可以测试咱们的用户是否有相应的权限! 测试 当没有删除权限时,删除报错 需要的结果是,点击删除,删除失败,后台返回没有权限的结果 原因,在请求是Ajax请求时,这个删除是Ajax,后台响应返回的就是一个没有权限时的我们自己设置的页面,他把整个全返回了,我们仅仅需要一个失败原因 想要的是json的结果字符串。。
咱们所有的请求可以分为两大类,一个是跳转页面,xxx/index 还有一类是Ajax请求期望返回的是{“success”:false,”message”:”没有权限”}
区分处理是否是Ajax请求,普通跳转页面的请求 ,就跳转没有权限的页面,如果是ajax请求返回{“success”:false,”message”:”没有权限”}
怎么判断Ajax 判断请求头里面是否有X-Requested-With 跳转页面请求头 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019071322375524.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg2MDc5MA==,size_16,color_FFFFFF,t_70)
Ajax请求多个一个请求头X-Requested-With XMLHttpRequest * 所以自定义拦截器 因为shiro自带的拦截器不区分请求,所以不能达到需要的请求需要结果
/**
* 将自定义过滤器交给shiro,改变shiro的默认过滤器配置
* */
//继承PermissionsAuthorizationFilter ,覆写方法
public class AISellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
Subject subject = this.getSubject(request, response);
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if (subject.getPrincipal() == null) {
this.saveRequestAndRedirectToLogin(request, response);
} else {
//根据请求确定是什么请求
String xRequestedWith = httpServletRequest.getHeader("X-Requested-With");
if (xRequestedWith != null &&"XMLHttpRequest".equals(xRequestedWith)) {
//3.在这里就代表是ajax请求
//表示ajax请求 {"success":false,"message":"没有权限"}
//返回结果
httpServletResponse.setContentType("text/json; charset=UTF-8");
httpServletResponse.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
}else {
String unauthorizedUrl = this.getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(401);
}
}
}
return false;
}
}
把过滤器交给shiro,配置applicationContext-shiro.xml
修改自动权限设置的工具类FilterChainDefinitionMapFactory 修改权限 linkedHashMap.put(e.getUrl(), “permsFilter[”+e.getSn()+"]")
public class FilterChainDefinitionMapFactory {
@Autowired
private IPermissionService iPermissionService;
public LinkedHashMap builderFilterChainDefinitionMap(){
LinkedHashMap linkedHashMap = new LinkedHashMap();
/*
/s/login.jsp = anon
/login = anon
/dept/index = perms[dept:index]
/employee/index = perms[employee:index]
/** = authc
* */
//anon 不需要登陆就可以访问 authc需要登陆才可以访问 per[xx:xx]需要授权才可以
linkedHashMap.put("/s/login.jsp", "anon");
linkedHashMap.put("/login", "anon");
linkedHashMap.put("*.js","anon");
linkedHashMap.put("*.css","anon");
linkedHashMap.put("/css/**","anon");
linkedHashMap.put("/js/**","anon");
linkedHashMap.put("/easyui/**","anon");
linkedHashMap.put("/images/**","anon");
//linkedHashMap.put("/dept/index", "perms[dept:index]");
//linkedHashMap.put("/employee/index", "perms[employee:index]");
List list = iPermissionService.findAll();
System.out.println(list);
list.forEach(e->
linkedHashMap.put(e.getUrl(), "permsFilter["+e.getSn()+"]")
);
linkedHashMap.put("/**", "authc");
return linkedHashMap;
}
}
五、菜单读取
没有url的就是父菜单,根目录。。 用户->角色->权限->菜单 用户有哪些权限,就应该有对应的菜单 (这里咱们可以分析数据库理解设计) 用户拥有对应的权限就拥有对应的菜单(二级菜单),如果此菜单有父菜单(一级菜单)也同时拥有
Domain设计
@Entity
@Table(name="menu")
public class Menu extends BaseDomain {
private String name;//菜单名称
private String url; //路径
private String icon; //图标
/**
* JsonIgnore:生成JSON的时候忽略这个属性
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="parent_id")
@JsonIgnore //这里生成json的时候要忽略,否则会造成功能相互调用
private Menu parent;
/**
* 还要配置一个一对多
* 这个字段不要交给JPA管理【到时候自己写代码管理】
* 数据库的menu表中就应该有一个children,而且还是List类型
* Transient:临时属性(JPA不管这个属性,和数据库没有关系)
*/
@Transient
private List children = new ArrayList();
…
public String getText(){ //EasyUI的树需要一个text属性
return name;
}
}
因为前台的名字是叫Text public String getText(){ //EasyUI的树需要一个text属性 return name;}
多个权限可以对应一个菜单 比如系统管理菜单下面可以有员工的管理等等 数据管理菜单也可以管理员工。。等等 所以权限和菜单是多对一关系
Permission类
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="menu_id")
private Menu menu;
MenuRepository 员工和角色多对多,一个员工可能有多个角色 一个角色可能多个权限,每一个权限对应一个menu 每一个权限permission对象中就有一个menu对象 每一个menu对象都有一个parent字段, 一个人通过permission去查menu,查到的menu只有子菜单, Permission 中的 menu 全是子菜单 只要有子菜单就一定有父菜单
比如一个user,有员工管理权力8,查询得到的是id为8的menu对象,这个menu一定有一个父菜单字段parent,6号
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019071322512312.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDg2MDc5MA==,size_16,color_FFFFFF,t_70) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190713225118550.png)
MenuRepository
@Query("select p.sn from Employee o join o.roles r join r.permissions p where o.id=?1")
Set findPermissionSnByUserID(Long id);
IMenuService
List findByUserId();
MenuServiceImpl
@Override
public List findByUserId() {
Employee loginUser = UserContext.getUser();
//拿到所有的菜单menu对象
List menuList = menuRepository.findByUserId(loginUser.getId());
//准备一个父菜单容器 仅仅是存入子菜单返回给前台
List parentList = new ArrayList();
//遍历查询的所以菜单
for (int i = 0;i |